#include "sanitizer_termination.h"
#include "stdlib.h"
#include "common_flags.h"
#include "string.h"
#include "asan_thread.h"
#include "asan_interceptors.h"
#include "asan_alloctor.h"


namespace __sanitizer{

static const int kMaxNumOfInternalDieCallbacks = 5;
static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];

void Abort() { abort(); }

bool AddDieCallback(DieCallbackType callback) {
  for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
    if (InternalDieCallbacks[i] == nullptr) {
      InternalDieCallbacks[i] = callback;
      return true;
    }
  }
  return false;
}

bool RemoveDieCallback(DieCallbackType callback) {
  for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
    if (InternalDieCallbacks[i] == callback) {
      memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
                       sizeof(InternalDieCallbacks[0]) *
                           (kMaxNumOfInternalDieCallbacks - i - 1));
      InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
      return true;
    }
  }
  return false;
}

void NORETURN Die() {
  VReport(ASAN_LOG_DEBUG, "Dieing\n");
  // TODO: support user callback
  for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
    if (InternalDieCallbacks[i])
      InternalDieCallbacks[i]();
  }
  if (common_flags()->abort_on_error)
    Abort();
  exit(common_flags()->exitcode);
  // no need to clear shadow and allocator
  // if (allocatorPtr != nullptr) {
  //   allocatorPtr->OnDelete();
  //   REAL(free(allocatorPtr));
  // }
}

void NORETURN CheckFailed(const char *file, int line, const char *cond,
                          u64 v1, u64 v2) {
  u32 tid = GetCurrentTid();
  Printf("%s: CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx) (tid=%u)\n",
         SanitizerToolName, file, line, cond, (uptr)v1,
         (uptr)v2, tid);
  // TODO: wait other threads to exit with printed error
  Die();
}

} // namespace __sanitizer

